﻿using Microsoft.Extensions.DependencyInjection;

namespace FlexField.Metadata
{
    public class MetadataBuilder
    {
        /// <summary>
        /// Creates a new instance of <see cref="MetadataBuilder"/>.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection"/> to attach to.</param>
        /// <param name="keyType">The type used for the primary key for the fields and entities.</param>
        public MetadataBuilder(IServiceCollection services, Type keyType)
        {
            Services = services;
            KeyType = keyType;
        }

        /// <summary>
        /// Gets the type used for the primary key for the fields and entities.
        /// </summary>
        public Type KeyType { get; }

        /// <summary>
        /// Gets the <see cref="IServiceCollection"/> services are attached to.
        /// </summary>
        /// <value>
        /// The <see cref="IServiceCollection"/> services are attached to.
        /// </value>
        public IServiceCollection Services { get; }

        private MetadataBuilder AddScoped(Type serviceType, Type concreteType)
        {
            Services.AddScoped(serviceType, concreteType);
            return this;
        }

        /// <summary>
        /// Adds an <see cref="IEntityValidator{TKey}"/> for the metadata entity.
        /// </summary>
        /// <typeparam name="TValidator">The entity validator type.</typeparam>
        /// <returns>The current <see cref="MetadataBuilder"/> instance.</returns>
        public virtual MetadataBuilder AddTypeValidator<TValidator>() where TValidator : class
            => AddScoped(typeof(IEntityValidator<>).MakeGenericType(KeyType), typeof(TValidator));

        /// <summary>
        /// Adds an <see cref="IEntityValidator{TKey}"/> for the metadata field.
        /// </summary>
        /// <typeparam name="TValidator">The field validator type.</typeparam>
        /// <returns>The current <see cref="MetadataBuilder"/> instance.</returns>
        public virtual MetadataBuilder AddFieldValidator<TValidator>() where TValidator : class
            => AddScoped(typeof(IFieldValidator<>).MakeGenericType(KeyType), typeof(TValidator));

        /// <summary>
        /// Adds an <see cref="MetadataErrorDescriber"/>.
        /// </summary>
        /// <typeparam name="TDescriber">The type of the error describer.</typeparam>
        /// <returns>The current <see cref="MetadataBuilder"/> instance.</returns>
        public virtual MetadataBuilder AddErrorDescriber<TDescriber>() where TDescriber : MetadataErrorDescriber
        {
            Services.AddScoped<MetadataErrorDescriber, TDescriber>();
            return this;
        }

        /// <summary>
        /// Adds an <see cref="IEntityStore{TKey}"/> for the metadata entity.
        /// </summary>
        /// <typeparam name="TStore">The entity store type.</typeparam>
        /// <returns>The current <see cref="MetadataBuilder"/> instance.</returns>
        public virtual MetadataBuilder AddTypeStore<TStore>() where TStore : class 
            => AddScoped(typeof(IEntityStore<>).MakeGenericType(KeyType), typeof(TStore));

        /// <summary>
        /// Adds an <see cref="IFieldStore{TKey}"/> for the metadata field.
        /// </summary>
        /// <typeparam name="TStore">The field store type.</typeparam>
        /// <returns>The current <see cref="MetadataBuilder"/> instance.</returns>
        public virtual MetadataBuilder AddFieldStore<TStore>() where TStore : class
            => AddScoped(typeof(IFieldStore<>).MakeGenericType(KeyType), typeof(TStore));

        /// <summary>
        /// Adds a <see cref="MetadataManager{TKey}"/>.
        /// </summary>
        /// <typeparam name="TManager">The type of the metadata manager to add.</typeparam>
        /// <returns>The current <see cref="MetadataBuilder"/> instance.</returns>
        public virtual MetadataBuilder AddMetadataManager<TManager>() where TManager : class
            => AddScoped(typeof(MetadataManager<>).MakeGenericType(KeyType), typeof(TManager));
    }
}
